import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import org.apache.poi.ss.usermodel.Row
import org.apache.poi.ss.usermodel.WorkbookFactory
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.w3c.dom.Element
import java.awt.Frame
import javax.swing.JFileChooser
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.FileWriter
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult

fun main() = application {
    Window(onCloseRequest = ::exitApplication, title = "Excel提取Android文言替换工具    by author:Zhong.ZiHao") {
        App()
    }
}

@Composable
fun App() {
    var excelPath by remember { mutableStateOf("") }
    var xmlPath by remember { mutableStateOf("") }
    var message by remember { mutableStateOf("") }
    var missingIdsMessage by remember { mutableStateOf("") }
    var unmatchedEntries by remember { mutableStateOf(emptyList<ExcelEntry>()) }
    var canExport by remember { mutableStateOf(false) }

    MaterialTheme {
        Column(modifier = Modifier.padding(16.dp)) {
            Row(verticalAlignment = Alignment.CenterVertically) {
                TextField(
                    value = excelPath,
                    onValueChange = { excelPath = it },
                    label = { Text("请输入Excel文件路径，需xlsx格式") },
                    modifier = Modifier.weight(1f).fillMaxWidth().padding(end = 8.dp)
                )
                Button(onClick = { excelPath = openFileDialog("Select Excel File", arrayOf("xlsx")) }) {
                    Text("浏览")
                }
            }
            Spacer(modifier = Modifier.height(8.dp))
            Row(verticalAlignment = Alignment.CenterVertically) {
                TextField(
                    value = xmlPath,
                    onValueChange = { xmlPath = it },
                    label = { Text("请输入要替换文言的Strings.xml文件路径") },
                    modifier = Modifier.weight(1f).fillMaxWidth().padding(end = 8.dp)
                )
                Button(onClick = { xmlPath = openFileDialog("Select XML File", arrayOf("xml")) }) {
                    Text("浏览")
                }
            }
            Spacer(modifier = Modifier.height(16.dp))
            Row(verticalAlignment = Alignment.CenterVertically) {
                Button(
                    onClick = {
                        try {
                            validateFilePaths(excelPath, xmlPath)
                            val (missingIds, newUnmatchedEntries) = replaceStringsFromExcel(excelPath, xmlPath)
                            message = "Files processed successfully!"
                            if (missingIds.isNotEmpty()) {
                                missingIdsMessage =
                                    "IDs not found in strings.xml total ${missingIds.size}:\n${missingIds.joinToString("\n")}"
                            } else {
                                missingIdsMessage = ""
                            }
                            unmatchedEntries = newUnmatchedEntries
                            canExport = unmatchedEntries.isNotEmpty()
                        } catch (e: Exception) {
                            message = "Error processing files: ${e.message}"
                            missingIdsMessage = ""
                            canExport = false
                        }
                    },
                ) {
                    Text("开始处理")
                }
                Spacer(modifier = Modifier.width(16.dp))
                if (canExport) {
                    Button(
                        onClick = {
                            val outputPath = saveFileDialog("Save Unmatched Entries as Excel", arrayOf("xlsx"))
                            if (outputPath.isNotEmpty()) {
                                writeUnmatchedEntriesToExcel(unmatchedEntries, outputPath)
                                message += "\nUnmatched entries exported to $outputPath"
                            }
                        },
                        enabled = canExport,
                    ) {
                        Text("导出缺失文言")
                    }
                }
            }
            Spacer(modifier = Modifier.height(16.dp))
            Text(message, textAlign = TextAlign.Center)
            Spacer(modifier = Modifier.height(16.dp))
            Box(modifier = Modifier.fillMaxSize().weight(1f)) {
                if (missingIdsMessage.isNotEmpty()) {
                    ScrollableText(missingIdsMessage)
                } else {
                    Text("No missing IDs.", textAlign = TextAlign.Center)
                }
            }
        }
    }
}

@Composable
fun ScrollableText(text: String) {
    val scrollState = rememberScrollState()

    Surface(
        elevation = 4.dp,
        modifier = Modifier.fillMaxSize()
    ) {
        Column(
            modifier = Modifier.verticalScroll(scrollState).padding(16.dp)
        ) {
            Text(text)
        }
    }
}

private fun validateFilePaths(excelPath: String, xmlPath: String) {
    val excelFile = File(excelPath)
    val xmlFile = File(xmlPath)

    if (!excelFile.exists() || !excelFile.isFile) {
        throw IllegalArgumentException("Excel file does not exist or is not a valid file.")
    }
    if (!xmlFile.exists() || !xmlFile.isFile) {
        throw IllegalArgumentException("strings.xml file does not exist or is not a valid file.")
    }
    if (!excelPath.toLowerCase().endsWith(".xlsx")) {
        throw IllegalArgumentException("Excel file must have .xlsx extension.")
    }
    if (!xmlPath.toLowerCase().endsWith(".xml")) {
        throw IllegalArgumentException("strings.xml file must have .xml extension.")
    }
}

//替换string.xml文言， 并返回一个Pair<缺失文言和使用同义中文替换的输出内容集合， 需要导出missing文言到Excel的集合>
private fun replaceStringsFromExcel(excelPath: String, xmlPath: String): Pair<List<String>, List<ExcelEntry>> {
    val missingIds = mutableListOf<String>()
    val entries = mutableListOf<ExcelEntry>()
    val unmatchedEntries = mutableListOf<ExcelEntry>() //记录没有匹配到的条目需要导出的条目
    println("-----excelPath:$excelPath ----- xmlPath:$xmlPath")
    try {
        FileInputStream(excelPath).use { fis ->
            XSSFWorkbook(fis).use { workbook ->
                val sheet = workbook.getSheetAt(0)
                val headerRow = sheet.getRow(0)
                val idIndex = findColumnIndex(headerRow, "id")
                val textIndex = findColumnIndex(headerRow, "text")
                val chineseIndex = findColumnIndex(headerRow, "chinese")

                if (idIndex == -1 || textIndex == -1 || chineseIndex == -1) {
                    throw IllegalArgumentException("Excel file must contain 'id', 'text', and 'chinese' columns.")
                }

                for (rowNum in 1 until sheet.lastRowNum + 1) {
                    val row = sheet.getRow(rowNum)
                    if (row != null) {
                        val idCell = row.getCell(idIndex)
                        val textCell = row.getCell(textIndex)
                        val chineseCell = row.getCell(chineseIndex)
                        if (idCell != null && textCell != null) {
                            val idValue = idCell.toString()
                            val textValue = textCell.toString().trim()
                            val chineseValue = chineseCell?.toString()?.trim()
                            if (textValue.isNotEmpty()) {
                                entries.add(ExcelEntry(idValue, textValue, chineseValue))
                            }
                        }
                    }
                }
            }
        }

        val documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
        val document = documentBuilder.parse(File(xmlPath))

        // Normalize the XML structure
        document.documentElement.normalize()

        // Check if the root element is <resources>
        if (document.documentElement.tagName != "resources") {
            throw IllegalArgumentException("The root element of strings.xml must be <resources>")
        }

        // Create maps for quick lookup
        val idToTextMap = entries.associateBy { it.id }
        val chineseToTextMap = entries.associateBy { it.chinese }

        // Find all <string> elements and update their values
        val stringElements = document.getElementsByTagName("string")
        for (i in 0 until stringElements.length) {
            val stringElement = stringElements.item(i) as Element
            val nameAttribute = stringElement.getAttributeNode("name")
            if (nameAttribute != null) {
                val idValue = nameAttribute.value
                val existingText = stringElement.textContent

                // First, check if the ID exists in the Excel file
                val entryById = idToTextMap[idValue]

                if (entryById != null) {
                    stringElement.textContent = entryById.text
                } else {
                    // If not, look for the Chinese content that matches the existing text
                    val matchingEntryByChinese = chineseToTextMap[existingText]
                    if (matchingEntryByChinese != null) {
                        stringElement.textContent = matchingEntryByChinese.text
                        missingIds.add("row-$i stringID:$idValue 找不到对应的ID，已找到id:${matchingEntryByChinese.id}相同的翻译并替换")
                    } else {
                        missingIds.add("row-$i stringID:$idValue 找不到对应的ID和中文")
                        unmatchedEntries.add(ExcelEntry(idValue, existingText, type = "string"))
                    }
                }
            }
        }

        // Find all <string-array>/<array> elements and update their item values
        fun processArrayElements(tagName: String) {
            val stringArrayElements = document.getElementsByTagName(tagName)
            for (i in 0 until stringArrayElements.length) {
                val stringArrayElement = stringArrayElements.item(i) as Element
                val nameAttribute = stringArrayElement.getAttributeNode("name")
                if (nameAttribute != null) {
                    val idValue = nameAttribute.value
                    val entryById = idToTextMap[idValue]
                    if (entryById != null) {
                        val items = entryById.text.split(",")
                        val itemElements = stringArrayElement.getElementsByTagName("item")
                        for (j in 0 until itemElements.length) {
                            if (j < items.size) {
                                (itemElements.item(j) as Element).textContent = items[j].trim()
                            } else {
                                // Handle cases where there are more items in XML than in Excel
                                missingIds.add("$idValue[item$j]")
                                unmatchedEntries.add(
                                    ExcelEntry(
                                        "$idValue[item$j]",
                                        (itemElements.item(j) as Element).textContent,
                                        type = tagName
                                    )
                                )
                            }
                        }
                        // Handle cases where there are more items in Excel than in XML
                        for (j in itemElements.length until items.size) {
                            missingIds.add("$idValue[item$j]")
                            unmatchedEntries.add(ExcelEntry("$idValue[item$j]", items[j].trim(), type = tagName))
                        }
                    } else {
                        // If not, look for the Chinese content that matches the existing text
                        val items = mutableListOf<String>()
                        val itemElements = stringArrayElement.getElementsByTagName("item")
                        for (j in 0 until itemElements.length) {
                            items.add((itemElements.item(j) as Element).textContent)
                        }
                        val combinedText = items.joinToString(",")
                        val matchingEntryByChinese = chineseToTextMap[combinedText]
                        if (matchingEntryByChinese != null) {
                            val newTextItems = matchingEntryByChinese.text.split(",")
                            for (j in 0 until itemElements.length) {
                                if (j < newTextItems.size) {
                                    (itemElements.item(j) as Element).textContent = newTextItems[j].trim()
                                } else {
                                    // Handle cases where there are more items in XML than in Excel
                                    missingIds.add("stringArrayID:$idValue[item$j] 找不到对应内容")
                                    unmatchedEntries.add(
                                        ExcelEntry(
                                            "$idValue[item$j]",
                                            (itemElements.item(j) as Element).textContent,
                                            type = tagName
                                        )
                                    )
                                }
                            }
                            // Handle cases where there are more items in Excel than in XML
                            for (j in itemElements.length until newTextItems.size) {
                                missingIds.add("stringArrayID:$idValue[item$j] 找不到对应内容")
                                unmatchedEntries.add(
                                    ExcelEntry(
                                        "$idValue[item$j]",
                                        newTextItems[j].trim(),
                                        type = tagName
                                    )
                                )
                            }
                        } else {
                            for (j in 0 until itemElements.length) {
                                missingIds.add("stringArrayID:$idValue[item$j] 找不到对应内容")
                                unmatchedEntries.add(
                                    ExcelEntry(
                                        "$idValue[item$j]",
                                        (itemElements.item(j) as Element).textContent,
                                        type = tagName
                                    )
                                )
                            }
                        }
                    }
                }
            }
        }

        // Process both string-array and array elements
        processArrayElements("string-array")
        processArrayElements("array")

        // Write the updated XML back to the file
        val transformerFactory = TransformerFactory.newInstance()
        val transformer = transformerFactory.newTransformer()
        val source = DOMSource(document)
        val result = StreamResult(FileWriter(File(xmlPath)))
        transformer.transform(source, result)
    } catch (e: Exception) {
        throw RuntimeException("Failed to process Excel or XML file", e)
    }

    return Pair(missingIds, unmatchedEntries)
}

fun processArrayElements(tagName: String) {

}

private fun writeUnmatchedEntriesToExcel(entries: List<ExcelEntry>, outputPath: String) {
    val workbook = XSSFWorkbook()
    val sheet = workbook.createSheet("Unmatched Entries")

    // Create header row
    val headerRow = sheet.createRow(0)
    headerRow.createCell(0).setCellValue("id")
    headerRow.createCell(1).setCellValue("text")
    headerRow.createCell(2).setCellValue("type")

    // Populate data rows
    entries.forEachIndexed { rowIndex, entry ->
        val row = sheet.createRow(rowIndex + 1)
        row.createCell(0).setCellValue(entry.id)
        row.createCell(1).setCellValue(entry.text)
        row.createCell(2).setCellValue(entry.type)
    }

    // Auto-size columns
    for (i in 0..1) {
        sheet.autoSizeColumn(i)
    }

    FileOutputStream(outputPath).use { fos ->
        workbook.write(fos)
    }
}

private fun findColumnIndex(headerRow: Row?, columnName: String): Int {
    headerRow?.forEachIndexed { index, cell ->
        if (cell.cellType == org.apache.poi.ss.usermodel.CellType.STRING && cell.stringCellValue.equals(
                columnName,
                ignoreCase = true
            )
        ) {
            return index
        }
    }
    return -1
}

private fun openFileDialog(title: String, extensions: Array<String>): String {
    val frame = Frame()
    val fileChooser = JFileChooser()
    fileChooser.dialogTitle = title
    fileChooser.fileFilter = object : javax.swing.filechooser.FileFilter() {
        override fun accept(file: File): Boolean {
            if (file.isDirectory) return true
            val fileName = file.name.toLowerCase()
            return extensions.any { fileName.endsWith(".$it") }
        }

        override fun getDescription(): String {
            return "${extensions.joinToString(", ")} files"
        }
    }

    val result = fileChooser.showOpenDialog(frame)
    return if (result == JFileChooser.APPROVE_OPTION) {
        fileChooser.selectedFile.absolutePath
    } else {
        ""
    }
}

private fun saveFileDialog(title: String, extensions: Array<String>): String {
    val frame = Frame()
    val fileChooser = JFileChooser()
    fileChooser.dialogTitle = title
    fileChooser.fileFilter = object : javax.swing.filechooser.FileFilter() {
        override fun accept(file: File): Boolean {
            if (file.isDirectory) return true
            val fileName = file.name.toLowerCase()
            return extensions.any { fileName.endsWith(".$it") }
        }

        override fun getDescription(): String {
            return "${extensions.joinToString(", ")} files"
        }
    }

    val result = fileChooser.showSaveDialog(frame)
    return if (result == JFileChooser.APPROVE_OPTION) {
        val selectedFile = fileChooser.selectedFile
        if (!selectedFile.name.endsWith(".xlsx")) {
            selectedFile.path + ".xlsx"
        } else {
            selectedFile.absolutePath
        }
    } else {
        ""
    }
}